use std::result::Result;

use futures::{AsyncRead, AsyncWrite};

mod quic;

pub use quic::{QuicEndpoint, QuicEndpointBuilder};

pub trait OutgoingBiStreamsTrait {
    type Error;
    type SendStream;
    type RecvStream;

    async fn open_bi(&self) -> Result<(Self::SendStream, Self::RecvStream), Self::Error>;
}

pub trait Endpoint {
    type Error: std::error::Error;
    type DomainIdentity;
    type EndpointIdentity;
    type EndpointAddr;
    type PrivateKey;
    type PublicKey;
    type DomainIncoming: futures::Stream<Item = (Self::OutgoingBiStreams, Self::IncomingBiStreams)>;
    type OutgoingBiStreams: OutgoingBiStreamsTrait<
        Error = Self::Error,
        SendStream = Self::SendStream,
        RecvStream = Self::RecvStream,
    >;
    type IncomingBiStreams: futures::Stream<
        Item = Result<(Self::SendStream, Self::RecvStream), Self::Error>,
    >;
    type SendStream: AsyncWrite;
    type RecvStream: AsyncRead;

    async fn listen_for_domain(
        &self,
        domain_identity: &Self::DomainIdentity,
        endpoint_identity: &Self::EndpointIdentity,
    ) -> Result<Self::DomainIncoming, Self::Error>;

    async fn connect_for_domain(
        &self,
        peer_addr: &Self::EndpointAddr,
        domain_identity: &Self::DomainIdentity,
        endpoint_identity: &Self::EndpointIdentity,
    ) -> Result<(Self::OutgoingBiStreams, Self::IncomingBiStreams), Self::Error>;

    async fn connect_for_p2p(
        &self,
        peer_addr: &Self::EndpointAddr,
        identity: &str,
        local_key: &Self::PrivateKey,
        peer_key: &Self::PublicKey,
        active: bool,
    ) -> Result<(Self::OutgoingBiStreams, Self::IncomingBiStreams), Self::Error>;

    async fn wait_idle(&self);
}
